package com.solarfridge.monitor.client;

import java.util.Date;
import java.util.HashMap;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.solarfridge.monitor.shared.LoginInfo;
import com.solarfridge.monitor.shared.data.Fridge;

public class AdminPageBinder extends Composite {

	private static AdminPageBinderUiBinder uiBinder = GWT
			.create(AdminPageBinderUiBinder.class);

	interface AdminPageBinderUiBinder extends UiBinder<Widget, AdminPageBinder> {
	}

	private final static String BASE_ID = "Id: ";
	private final static String BASE_LC = "Last Checkin: ";
	private final static String BASE_DA = "Date Added:";
	
	private final DataServiceAsync mDataService = 
		DataService.Util.getInstance();
	//map containing the list of fridges by id.
	final HashMap<String, Fridge> mFridgeMap = new HashMap<String, Fridge>();
	
	
	@UiField
	Button btnNewFridge;
	@UiField
	Button btnSave;
	@UiField
	Label lblId;
	@UiField
	Label lblLastCheck;
	@UiField
	ListBox fridgeList;
	@UiField 
	Anchor linkSignOut;
	@UiField 
	PushButton btnSaveAdmin;
	@UiField
	CheckBox cboxLogging;
	@UiField 
	Label lblDateAdded;

	
	public AdminPageBinder(LoginInfo loginInfo) {
		initWidget(uiBinder.createAndBindUi(this));
		
		lblId.setText(BASE_ID);
		lblLastCheck.setText(BASE_LC);
		linkSignOut.setHref(loginInfo.getLogoutUrl());
	}


	/**
	 * Populates the fridge list from the datastore
	 */
	public void populateFridgeList(){
		mDataService.getFridgeList(new AsyncCallback<Fridge[]>() {			
			@Override
			public void onSuccess(Fridge[] result) {
				populateFridgeList(result);
			}
			
			@Override
			public void onFailure(Throwable caught) {
				Window.alert(caught.getMessage());
			}
		});
	}
	
	/**
	 * Gets the fridge currently selected in the ListBox.
	 * @return Null if no fridge is selected.
	 */
	private Fridge getSelectedFridge(){
		int selectedIndex = fridgeList.getSelectedIndex();
		if (selectedIndex == -1) return null;
		String selectedId = fridgeList.getItemText(selectedIndex);
		Fridge selectedFridge = mFridgeMap.get(selectedId);
		return selectedFridge;
	}
	
	private void onFridgeInfoChanged() {
		setButtonEnabled(btnSave, true);
	}
	
	private void setButtonEnabled(Button btn, boolean enabled) {
		btn.setEnabled(enabled);
		//TODO implement a fix for firefox and chrome.
	}
	
	public void populateFridgeList(Fridge[] fridges) {
		mFridgeMap.clear();
		fridgeList.clear();
		for(Fridge fridge : fridges){
			fridgeList.addItem(fridge.getFridgeId());
			mFridgeMap.put(fridge.getFridgeId(), fridge);
		}
		
	}
	
	private DialogBox createProcessingDialog() {
		final DialogBox processingDialog = new DialogBox(false);
		processingDialog.setText("Processing...");
		processingDialog.center();
		
		return  processingDialog;
	}

	@UiHandler("btnNewFridge")
	void onBtnNewFridgeClick(ClickEvent event) {
		class NewFridgeDialog extends DialogBox {
			public NewFridgeDialog(){
				final NewFridgeDialog mThis = this;
				setText("Create New Fridge...");
				
				VerticalPanel mainPanel = new VerticalPanel();
				
				HorizontalPanel idPanel = new HorizontalPanel();
				idPanel.add(new Label("Fridge Id: "));
				final TextBox txtFridgeId = new TextBox();
				idPanel.add(txtFridgeId);
				mainPanel.add(idPanel);
				
				HorizontalPanel logPanel = new HorizontalPanel();
				logPanel.add(new Label("Activate Logging? "));
				final CheckBox cboxLogging = new CheckBox();
				cboxLogging.setValue(true);
				logPanel.add(cboxLogging);
				mainPanel.add(logPanel);
				
				HorizontalPanel btnPanel = new HorizontalPanel();
				Button btnSave = new Button("Save");
				btnSave.addClickHandler(new ClickHandler() {					
					@Override
					public void onClick(ClickEvent event) {
						final Fridge fridgeToCreate = new Fridge(txtFridgeId.getText(), 
								cboxLogging.getValue());
						final DialogBox processingDialog = createProcessingDialog();
						processingDialog.show();
						mDataService.saveFridge(fridgeToCreate, new AsyncCallback<Integer>() {							
							@Override
							public void onSuccess(Integer result) {
								processingDialog.setAnimationEnabled(true);
								switch (result) {
								case DataService.NAME_TAKEN:
									processingDialog.setText("Id Taken");
									processingDialog.add(new Label("A fridge with that id" +
											"already exists. Please use another id."));
									fridgeList.addItem(fridgeToCreate.getFridgeId());
									mFridgeMap.put(fridgeToCreate.getFridgeId(), fridgeToCreate);
									break;
								case DataService.SUCCESS:
									processingDialog.hide();
									mThis.hide();
									populateFridgeList();
									break;
								default:
									processingDialog.setText("An error occured");
									processingDialog.add(new Label("The server returned an unknown code: " + 
											String.valueOf(result)));
									break;
								}								
							}
							
							@Override
							public void onFailure(Throwable caught) {
								processingDialog.setAnimationEnabled(true);
								processingDialog.setText("An error occured");
								processingDialog.add(new Label("An error occured while contacting \n"
										+"the server. Please try again later."));
							}
						});
					}
				});
				btnPanel.add(btnSave);
				Button btnDiscard = new Button("Discard");
				btnDiscard.addClickHandler(new ClickHandler() {					
					@Override
					public void onClick(ClickEvent event) {
						mThis.hide();						
					}
				});
				btnPanel.add(btnDiscard);
				mainPanel.add(btnPanel);
				
				this.add(mainPanel);
				
			}
		}
		NewFridgeDialog dialog = new NewFridgeDialog();
		dialog.center();
		dialog.show();
	}
	@UiHandler("fridgeList")
	void onFridgeListClick(ClickEvent event) {
		setButtonEnabled(btnSave, false);
		Fridge selectedFridge = getSelectedFridge();
		
		if (selectedFridge == null) return;
		
		lblId.setText(BASE_ID + selectedFridge.getFridgeId());
		if(selectedFridge.getLastCheckin() != -1) {
			Date lastCheck = new Date(selectedFridge.getLastCheckin());
			lblLastCheck.setText(BASE_LC + lastCheck.toString());
		} else {
			lblLastCheck.setText(BASE_LC + "Never");
		}
		
		Date dateAdded = new Date(selectedFridge.getDateAdded());
		lblDateAdded.setText(BASE_DA + dateAdded.toString());
		
		cboxLogging.setValue(selectedFridge.isLoggingEnabled());
	}
	
	@UiHandler("btnSave")
	void onBtnSaveClick(ClickEvent event) {
		final Fridge fridgeToUpdate = getSelectedFridge();
		if (fridgeToUpdate == null) return;
		fridgeToUpdate.setLoggingEnabled(cboxLogging.getValue());

		final DialogBox pDialog = createProcessingDialog();
		pDialog.show();
		
		mDataService.updateFridge(fridgeToUpdate, new AsyncCallback<Integer>() {
			@Override
			public void onFailure(Throwable caught) {
				pDialog.setAutoHideEnabled(true);
				pDialog.setText("An error occured");
				pDialog.add(new Label("An error occured while contacting \n"
						+"the server. Please try again later."));				
			}

			@Override
			public void onSuccess(Integer result) {
				pDialog.setAutoHideEnabled(true);
				switch (result) {
				case DataService.NAME_TAKEN:
					pDialog.setText("Id Taken");
					pDialog.add(new Label("A fridge with that id" +
							"already exists. Please use another id."));
					fridgeList.addItem(fridgeToUpdate.getFridgeId());
					mFridgeMap.put(fridgeToUpdate.getFridgeId(), fridgeToUpdate);
					break;
				case DataService.SUCCESS:
					pDialog.hide();
					populateFridgeList();
					break;
				default:
					pDialog.setText("An error occured");
					pDialog.add(new Label("The server returned an unknown code: " + 
							String.valueOf(result)));
					break;
				}					
			}
		});
	}
	
	@UiHandler("cboxLogging")
	void onCboxLoggingValueChange(ValueChangeEvent<Boolean> event) {
		onFridgeInfoChanged();
	}
}
